/**
 * Copyright 2017 IBM All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
'use strict';
var path = require('path');
var fs = require('fs');
var util = require('util');
var hfc = require('fabric-client');
var Peer = require('fabric-client/lib/Peer.js');
var helper = require('./helper.js');
var logger = helper.getLogger('invoke-chaincode');
var EventHub = require('fabric-client/lib/EventHub.js');
var ORGS = hfc.getConfigSetting('network-config');

var invokeChaincode = function (peerNames, channelName, chaincodeName, fcn, args, username, org) {
    logger.debug(util.format('\n============ invoke transaction on organization %s ============\n', org));
    var client = helper.getClientForOrg(org);
    var channel = helper.getChannelForOrg(channelName, org);
    if (channel == null) {
        logger.error('===============channle is null======================== ');
        return;
    }
    var targets = (peerNames) ? helper.newPeers(peerNames, org) : undefined;
    var tx_id = null;
    var returnJsonStr = null;

//  username
    return helper.getOrgAdmin(org).then((user) => {
        // return helper.getRegisteredUsers(username, org).then((member) => {
        // logger.debug(user);
        tx_id = client.newTransactionID();
        // logger.debug(util.format('Sending transaction "%j"', tx_id));
        // send proposal to endorser
        var request = {
            chaincodeId: chaincodeName,
            fcn: fcn,
            args: args,
            chainId: channelName,
            txId: tx_id
        };

        if (targets) {
            request.targets = targets;
        }
        try {
            return channel.sendTransactionProposal(request);
        }
        catch (e) {
            console.log("==================================", e);
            return e;
        }

    }, (err) => {
        logger.error('Failed to enroll user \'' + username + '\'. ' + err);
        throw new Error('Failed to enroll user \'' + username + '\'. ' + err);
    }).then((results) => {
        console.log("++++++++++++++++++++++++++++++   99 ++++++++++++++++++++++++++++")
        //console.log(JSON.stringify(results));
        console.log("++++++++++++++++++++++++++++++   99 ++++++++++++++++++++++++++++")
        var proposalResponses = results[0];
        var proposal = results[1];
        var all_good = true;
        for (var i in proposalResponses) {
            let one_good = false;
            if (proposalResponses && proposalResponses[i].response &&
                proposalResponses[i].response.status === 200) {
                one_good = true;
                logger.info('transaction proposal was good');
            } else {
                logger.error({success:false,message:proposalResponses[i].response.message});
                return proposalResponses[i].response.message
            }
            all_good = all_good & one_good;
        }
        if (all_good) {
            if (returnJsonStr == null) {
                returnJsonStr = util.format("%s", proposalResponses[0].response.payload);
                // logger.debug("returnJsonStr:"+returnJsonStr);
            }
            var request = {
                proposalResponses: proposalResponses,
                proposal: proposal
            };
            // set the transaction listener and set a timeout of 30sec
            // if the transaction did not get committed within the timeout period,
            // fail the test
            var transactionID = tx_id.getTransactionID();
            var eventPromises = [];

            if (!peerNames) {
                peerNames = channel.getPeers().map(function (peer) {
                    return peer.getName();
                });
            }

            var eventhubs = helper.newEventHubs(peerNames, org);
            for (let key in eventhubs) {
                let eh = eventhubs[key];
                eh.connect();

                let txPromise = new Promise((resolve, reject) => {
                    let handle = setTimeout(() => {
                        eh.disconnect();
                        reject();
                    }, 30000);

                    eh.registerTxEvent(transactionID, (tx, code) => {
                        clearTimeout(handle);
                        eh.unregisterTxEvent(transactionID);
                        eh.disconnect();

                        if (code !== 'VALID') {
                            logger.error(
                                'The balance transfer transaction was invalid, code = ' + code);
                            reject();
                        } else {
                            logger.info(
                                'The balance transfer transaction has been committed on peer ' +
                                eh._ep._endpoint.addr);
                            resolve();
                        }
                    });
                });
                eventPromises.push(txPromise);
            };
            var sendPromise = channel.sendTransaction(request);
            return Promise.all([sendPromise].concat(eventPromises)).then((results) => {
                logger.debug(' event promise all complete and testing complete');
                return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call
            }).catch((err) => {
                logger.error(
                    'Failed to send transaction and get notifications within the timeout period.'
                );
                return 'Failed to send transaction and get notifications within the timeout period.';
            });
        } else {
            logger.error(
                'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'
            );
            return 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...';
        }
    }, (err) => {
        logger.error('Failed to send proposal due to error: ' + err.stack ? err.stack :
            err);
        return 'Failed to send proposal due to error: ' + err.stack ? err.stack :
            err;
    }).then((response) => {
        if (response.status === 'SUCCESS') {
            logger.info(response);
            logger.info('Successfully sent transaction to the orderer.');
            // logger.info(returnJsonStr);
            if (returnJsonStr !== null) {
                return returnJsonStr;
            }
            return response;
        } else {

            logger.error('Failed to order the transaction. Error code: ' + response.status);
            // return 'Failed to order the transaction. Error code: ' + response.status;
            return {success: false, message: "上链失败"};
        }
    }, (err) => {
        logger.error('Failed to send transaction due to error: ' + err.stack ? err
            .stack : err);
        return 'Failed to send transaction due to error: ' + err.stack ? err.stack :
            err;
    });
};

var invokeChaincodeCTK = function (peerNames, channelName, chaincodeName, fcn, args, username, org) {
    logger.debug(util.format('\n============ invoke transaction on organization %s ============\n', org));
    var client = helper.getClientForOrg(org);
    var channel = helper.getChannelForOrg(channelName, org);
    if (channel == null) {
        logger.error('===============channle is null======================== ');
        return;
    }
    var targets = (peerNames) ? helper.newPeers(peerNames, org) : undefined;
    var returnJsonStr = null;
    let tx_id = null;
//  username
    return helper.getOrgAdmin(org).then((user) => {
        // return helper.getRegisteredUsers(username, org).then((member) => {
        // logger.debug(user);
        tx_id = client.newTransactionID();
        // logger.debug(util.format('Sending transaction "%j"', tx_id));
        // send proposal to endorser
        var request = {
            chaincodeId: chaincodeName,
            fcn: fcn,
            args: args,
            chainId: channelName,
            txId: tx_id
        };

        if (targets) {
            request.targets = targets;
        }
        try {
            return channel.sendTransactionProposal(request);
        }
        catch (e) {
            console.log("==================================", e);
            return e;
        }

    }, (err) => {
        logger.error('Failed to enroll user \'' + username + '\'. ' + err);
        throw new Error('Failed to enroll user \'' + username + '\'. ' + err);
    }).then((results) => {
        console.log("++++++++++++++++++++++++++++++   99 ++++++++++++++++++++++++++++")
        console.log(JSON.stringify(results));
        console.log("++++++++++++++++++++++++++++++   99 ++++++++++++++++++++++++++++")
        var proposalResponses = results[0];
        var proposal = results[1];
        var all_good = true;
        for (var i in proposalResponses) {
            let one_good = false;
            if (proposalResponses && proposalResponses[i].response &&
                proposalResponses[i].response.status === 200) {
                one_good = true;
                logger.info('transaction proposal was good');
            } else {
                logger.error({success:false,message:proposalResponses[i].response.message});
                return proposalResponses[i].response.message
            }
            all_good = all_good & one_good;
        }
        if (all_good) {
            if (returnJsonStr == null) {
                returnJsonStr = util.format("%s", proposalResponses[0].response.payload);
                // logger.debug("returnJsonStr:"+returnJsonStr);
            }
            var request = {
                proposalResponses: proposalResponses,
                proposal: proposal
            };
            // set the transaction listener and set a timeout of 30sec
            // if the transaction did not get committed within the timeout period,
            // fail the test
            var transactionID = tx_id.getTransactionID();
            var eventPromises = [];

            if (!peerNames) {
                peerNames = channel.getPeers().map(function (peer) {
                    return peer.getName();
                });
            }

            var eventhubs = helper.newEventHubs(peerNames, org);
            for (let key in eventhubs) {
                let eh = eventhubs[key];
                eh.connect();

                let txPromise = new Promise((resolve, reject) => {
                    let handle = setTimeout(() => {
                        eh.disconnect();
                        reject();
                    }, 30000);

                    eh.registerTxEvent(transactionID, (tx, code) => {
                        clearTimeout(handle);
                        eh.unregisterTxEvent(transactionID);
                        eh.disconnect();

                        if (code !== 'VALID') {
                            logger.error(
                                'The balance transfer transaction was invalid, code = ' + code);
                            reject();
                        } else {
                            logger.info(
                                'The balance transfer transaction has been committed on peer ' +
                                eh._ep._endpoint.addr);
                            resolve();
                        }
                    });
                });
                eventPromises.push(txPromise);
            };
            var sendPromise = channel.sendTransaction(request);
            return Promise.all([sendPromise].concat(eventPromises)).then((results) => {
                logger.debug(' event promise all complete and testing complete');
                return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call
            }).catch((err) => {
                logger.error(
                    'Failed to send transaction and get notifications within the timeout period.'
                );
                return 'Failed to send transaction and get notifications within the timeout period.';
            });
        } else {
            logger.error(
                'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'
            );
            return 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...';
        }
    }, (err) => {
        logger.error('Failed to send proposal due to error: ' + err.stack ? err.stack :
            err);
        return 'Failed to send proposal due to error: ' + err.stack ? err.stack :
            err;
    }).then((response) => {
        if (response.status === 'SUCCESS') {
            logger.info('Successfully sent transaction to the orderer.');
            // logger.info(returnJsonStr);
            if (returnJsonStr !== null) {
                try {
                    return {success: true, message: "ok",data:JSON.parse(returnJsonStr)};
                } catch(e) {
                    console.log(e);
                    return {success: true, message:"success"};
                }
        
                
            }
            return response;
        } else {
            // return 'Failed to order the transaction. Error code: ' + response.status;
            return {success: false, message: response};
        }
    }, (err) => {
        logger.error('Failed to send transaction due to error: ' + err.stack ? err
            .stack : err);
        return 'Failed to send transaction due to error: ' + err.stack ? err.stack :
            err;
    });
};

exports.invokeChaincode = invokeChaincode;
exports.invokeChaincodeCTK = invokeChaincodeCTK;
